package tech.lq0.dreamaticvoyage.block.entity;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.EnergyStorage;
import org.jetbrains.annotations.Nullable;
import tech.lq0.dreamaticvoyage.block.machine.fukamizutech.tier2.FukamizuTurbineEngine;
import tech.lq0.dreamaticvoyage.capability.ModCapabilities;
import tech.lq0.dreamaticvoyage.capability.uce.UCEnergyStorage;
import tech.lq0.dreamaticvoyage.gui.menu.FukamizuTurbineEngineMenu;
import tech.lq0.dreamaticvoyage.gui.slot.ContainerEnergyData;
import tech.lq0.dreamaticvoyage.init.BlockEntityRegistry;
import tech.lq0.dreamaticvoyage.tools.FukamizuTechHelper;

import java.util.concurrent.atomic.AtomicBoolean;

public class FukamizuTurbineEngineBlockEntity extends BlockEntity implements MenuProvider {

    public static final int MIN_PRESSURE = 32;
    public static final int MAX_PRESSURE = 128;

    public static final int MAX_UCE_ENERGY = 30000;
    public static final int MAX_FE_ENERGY = 240000;

    public static final int DEFAULT_UCE_COST = 64;
    public static final int MAX_UCE_COST = 256;

    public static final int DEFAULT_FE_SPEED = 512;
    public static final int MAX_FE_SPEED = 2048;

    public static final int MAX_DATA_COUNT = 3;

    protected LazyOptional<UCEnergyStorage> uceHandler;
    protected LazyOptional<EnergyStorage> feHandler;

    public int pressure;

    protected final ContainerEnergyData dataAccess = new ContainerEnergyData() {

        @Override
        public long get(int pIndex) {
            return switch (pIndex) {
                case 0 -> FukamizuTurbineEngineBlockEntity.this.pressure;
                case 1 ->
                        FukamizuTurbineEngineBlockEntity.this.uceHandler.map(UCEnergyStorage::getEnergyStored).orElse(0);
                case 2 -> FukamizuTurbineEngineBlockEntity.this.feHandler.map(EnergyStorage::getEnergyStored).orElse(0);
                default -> 0L;
            };
        }

        @Override
        public void set(int pIndex, long pValue) {
            switch (pIndex) {
                case 0 -> FukamizuTurbineEngineBlockEntity.this.pressure = (int) pValue;
                case 1 ->
                        FukamizuTurbineEngineBlockEntity.this.uceHandler.ifPresent(handler -> handler.receiveEnergy((int) pValue, false));
                case 2 ->
                        FukamizuTurbineEngineBlockEntity.this.feHandler.ifPresent(fe -> fe.receiveEnergy((int) pValue, false));
            }
        }

        @Override
        public int getCount() {
            return MAX_DATA_COUNT;
        }
    };

    public FukamizuTurbineEngineBlockEntity(BlockPos pos, BlockState state) {
        super(BlockEntityRegistry.FUKAMIZU_TURBINE_ENGINE_BLOCK_ENTITY.get(), pos, state);

        this.uceHandler = LazyOptional.of(() -> new UCEnergyStorage(MAX_UCE_ENERGY));
        this.feHandler = LazyOptional.of(() -> new EnergyStorage(MAX_FE_ENERGY, MAX_FE_SPEED));
    }

    public static void serverTick(Level pLevel, BlockPos pPos, BlockState pState, FukamizuTurbineEngineBlockEntity blockEntity) {
        blockEntity.pressure = Math.min(FukamizuTechHelper.calculatePressure(pLevel, pPos), MAX_PRESSURE);

        AtomicBoolean flag = new AtomicBoolean(false);
        if (blockEntity.pressure < 32) {
            flag.set(true);
        } else {
            blockEntity.uceHandler.ifPresent(handler -> {
                if (handler.getEnergyStored() < DEFAULT_UCE_COST) {
                    flag.set(true);
                    return;
                }

                blockEntity.feHandler.ifPresent(fe -> {
                    if (fe.getEnergyStored() >= fe.getMaxEnergyStored()) {
                        flag.set(true);
                        return;
                    }

                    int pressure = Mth.clamp(blockEntity.pressure, MIN_PRESSURE, MAX_PRESSURE);
                    int uceCost = DEFAULT_UCE_COST + (pressure - 32) * 2;
                    int feSpeed = DEFAULT_FE_SPEED + (pressure - 32) * 16;

                    if (handler.getEnergyStored() < uceCost) {
                        flag.set(true);
                        return;
                    }

                    handler.extractEnergy(uceCost, false);
                    fe.receiveEnergy(feSpeed, false);
                    if (!pState.getValue(FukamizuTurbineEngine.PROCESSING)) {
                        pLevel.setBlockAndUpdate(pPos, pState.setValue(FukamizuTurbineEngine.PROCESSING, true));
                    }
                    blockEntity.setChanged();
                });
            });
        }

        if (flag.get() && pState.getValue(FukamizuTurbineEngine.PROCESSING)) {
            pLevel.setBlockAndUpdate(pPos, pState.setValue(FukamizuTurbineEngine.PROCESSING, false));
            setChanged(pLevel, pPos, pState);
        }
    }

    @Override
    public void load(CompoundTag pTag) {
        super.load(pTag);

        if (pTag.contains("UmisuEnergy")) {
            getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).ifPresent(handler -> ((UCEnergyStorage) handler).deserializeNBT(pTag.get("UmisuEnergy")));
        }
        if (pTag.contains("Energy")) {
            getCapability(ForgeCapabilities.ENERGY).ifPresent(handler -> ((EnergyStorage) handler).deserializeNBT(pTag.get("Energy")));
        }
        this.pressure = pTag.getInt("Pressure");
    }

    @Override
    protected void saveAdditional(CompoundTag pTag) {
        super.saveAdditional(pTag);

        getCapability(ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY).ifPresent(handler -> pTag.put("UmisuEnergy", ((UCEnergyStorage) handler).serializeNBT()));
        getCapability(ForgeCapabilities.ENERGY).ifPresent(handler -> pTag.put("Energy", ((EnergyStorage) handler).serializeNBT()));
        pTag.putInt("Pressure", this.pressure);
    }

    @Override
    public Component getDisplayName() {
        return Component.translatable("container.dreamaticvoyage.fukamizu_turbine_engine");
    }

    @Nullable
    @Override
    public AbstractContainerMenu createMenu(int pContainerId, Inventory pPlayerInventory, Player pPlayer) {
        if (this.level == null) return null;
        return new FukamizuTurbineEngineMenu(pContainerId, pPlayerInventory, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos()));
    }

    @Override
    public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
        if (cap == ModCapabilities.UMISU_CURRENT_ENERGY_CAPABILITY) {
            return uceHandler.cast();
        }
        if (cap == ForgeCapabilities.ENERGY && side != Direction.UP) {
            return feHandler.cast();
        }
        return super.getCapability(cap, side);
    }

    @Override
    public void invalidateCaps() {
        super.invalidateCaps();
        this.uceHandler.invalidate();
        this.feHandler.invalidate();
    }

    @Override
    public void reviveCaps() {
        super.reviveCaps();
        this.uceHandler = LazyOptional.of(() -> new UCEnergyStorage(MAX_UCE_ENERGY));
        this.feHandler = LazyOptional.of(() -> new EnergyStorage(MAX_FE_ENERGY, MAX_FE_SPEED));
    }
}
